package de.hci.mid.socialmap.web;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GlobalPosition;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import de.hci.mid.socialmap.entities.Event;
import de.hci.mid.socialmap.entities.ImageHolder;

//import org.gavaghan.geodesy.*;

@ApplicationPath("/service/")
@Path("json")
public class RESTfulService extends Application {
	@Context
	private UriInfo context;
	private String getEvents = "SELECT s FROM Event s";
	SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	Gson gs = new Gson();

	/**
	 * Default constructor.
	 */
	public RESTfulService() {

	}

	/**
	 * Save Entity into Database
	 * 
	 * @param content
	 * @return
	 */
	@PUT
	@Consumes("application/json")
	public int putJson(String content) {
		System.out.println("EventAdd Aufruf mit +\n" + content + "\n\n");
		Type type = new TypeToken<Event>() {
		}.getType();
		Event event = new Gson().fromJson(content, type);
		int id = 0;
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		em.getTransaction().begin();
		if (event.getId() == 0) {
			Event e = new Event(event.getName(), event.getMacid(), event.getStart(), event.getEnd(), event.getCords(),
					event.getDescription(), event.getCategories());
			em.persist(e);
			em.flush();
			id = e.getId();
			System.out.println("ADDED EVENT WITH ID: " + e.getId());
			em.getTransaction().commit();

		} else {
			Event eventx = em.find(Event.class, event.getId());
			eventx.setName(event.getName());
			eventx.setMacid(event.getMacid());
			eventx.setStart(event.getStart());
			eventx.setEnd(event.getEnd());
			eventx.setCords(event.getCords());
			eventx.setDescription(event.getDescription());
			eventx.setCategories(event.getCategories());
			eventx.setEdited(new Date());
			System.out.println("EDITIED Event mit id: " + eventx.getId());
			em.getTransaction().commit();
			id = event.getId();
		}
		em.close();
		return id;
	}

	/**
	 * Remove Entity from Database
	 * 
	 * @param id
	 * @return
	 */
	@PUT
	@Path("remove")
	public boolean removeEvent(@QueryParam("id") int id) {
		System.out.println("EventRemove Aufruf mit +\n" + id + "\n\n");
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		Event e = (Event) em.find(Event.class, id);
		ImageHolder i = (ImageHolder) em.find(ImageHolder.class, id);
		em.getTransaction().begin();
		if (i != null) {
			em.remove(i);
		}
		if (e == null) {
			return false;
		}
		System.out.println("Removing Event: " + e.getId());
		e.setDeleted(new Date());
		e.setEdited(new Date());
		em.getTransaction().commit();
		return true;
	}

	/**
	 * Retrieves all Events as JsonString
	 * 
	 * @return events as JsonString
	 */
	@GET
	@Produces("application/json")
	public String getJson() {
		System.out.println("Gebe alle Events die nicht alt sind zurück!\n\n");
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		@SuppressWarnings("unchecked")
		List<Event> events = (List<Event>) em.createQuery(getEvents).getResultList();
		em.close();
		List<Event> returnEvents = new ArrayList<Event>(events);
		for (Event e : events) {
			if (e.getEnd().before(new Date())) {
				returnEvents.remove(e);
			}
		}
		return gs.toJson(returnEvents);
	}

	/**
	 * Return filtered Events as Jsonstring.
	 * 
	 * @param location
	 *            as Geocoordinates (eg. 52.157663:9.947133)
	 * @param radius
	 *            as double radius in meter
	 * @param userUpdate
	 *            last UserUpdate. Format: "yyyy-MM-dd HH:mm:ss"
	 * @return filtered events.
	 */
	@PUT
	@Path("filtered")
	@Consumes("application/json")
	@Produces("application/json")
	public String getFiltered(String content, @QueryParam("location") String location, @QueryParam("radius") double radius,
			@QueryParam("time") String userUpdate) {
		System.out.println("Event getFilter Aufruf mit: +\nLocation: " + location + " Radius: " + radius + " Time: " + userUpdate + "\n\n");
		GeodeticCalculator geoCalc = new GeodeticCalculator();
		Ellipsoid reference = Ellipsoid.WGS84;
		String[] coords = location.split(":");
		GlobalPosition userPos = new GlobalPosition(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), 0.0);
		GlobalPosition eventPos = new GlobalPosition(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), 0.0);
		double distance;
		System.out.println(content);
		ArrayList<Integer> ids = null;
		Type type = new TypeToken<ArrayList<Integer>>() {
		}.getType();
		ids = new Gson().fromJson(content, type);
		Date lastUserUpdate = null;
		try {
			lastUserUpdate = df.parse(userUpdate);
		} catch (ParseException e1) {
			System.out.println("Date Exception! :(");
		}
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		@SuppressWarnings("unchecked")
		List<Event> events = (List<Event>) em.createQuery(getEvents).getResultList();
		em.close();
		List<Event> returnEvents = new ArrayList<Event>(events);
		for (Event e : events) {
			String[] eventCoords = e.getCords().split(":");
			eventPos.setLatitude(Double.parseDouble(eventCoords[0]));
			eventPos.setLongitude(Double.parseDouble(eventCoords[1]));
			distance = geoCalc.calculateGeodeticCurve(reference, userPos, eventPos).getEllipsoidalDistance();
			if (distance > radius) {
				returnEvents.remove(e);
			}
			if (e.getEnd().before(new Date())) {
				returnEvents.remove(e);
			}
			if (ids != null) {
				if (ids.contains(e.getId())) {
					System.out.println("Zeitformat: e.getCreated = " + e.getCreated() + " // lastUserUpdate = " + lastUserUpdate);
					System.out.println("e.getCreated equals lastUserUpdate = "
							+ e.getCreated().toString().equals(lastUserUpdate.toString()));
					if (lastUserUpdate != null
							&& (e.getCreated().before(lastUserUpdate) || e.getCreated().toString().equals(lastUserUpdate.toString()))) {
						returnEvents.remove(e);
						System.out.println("Remove: " + e.getId());

					}
				}
			}
			if (ids == null || !ids.contains(e.getId())) {
				if (e.getDeleted() != null) {
					returnEvents.remove(e);
				}

			}
		}
		System.out.println(gs.toJson(returnEvents));
		return gs.toJson(returnEvents);

	}

	/**
	 * Return filterd edited Events as Jsonstring.
	 * 
	 * @param location
	 *            as Geocoordinates (eg. 52.157663:9.947133)
	 * @param radius
	 *            as double radius in meter
	 * @param userUpdate
	 *            last UserUpdate. Format: "yyyy-MM-dd HH:mm:ss"
	 * @return filtered events.
	 */
	@PUT
	@Path("edited")
	@Consumes("application/json")
	@Produces("application/json")
	public String getEdited(String content, @QueryParam("location") String location, @QueryParam("radius") double radius,
			@QueryParam("time") String userUpdate) {
		System.out.println("IDLISTE = " + content);
		System.out.println("Event getEdited Aufruf mit: +\nLocation: " + location + " Radius: " + radius + " Time: " + userUpdate + "\n\n");
		GeodeticCalculator geoCalc = new GeodeticCalculator();
		Ellipsoid reference = Ellipsoid.WGS84;
		String[] coords = location.split(":");
		GlobalPosition userPos = new GlobalPosition(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), 0.0);
		GlobalPosition eventPos = new GlobalPosition(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), 0.0);
		double distance;

		System.out.println(content);
		ArrayList<Integer> ids = null;
		Type type = new TypeToken<ArrayList<Integer>>() {
		}.getType();
		ids = new Gson().fromJson(content, type);
		Date lastUserUpdate = null;
		try {
			lastUserUpdate = df.parse(userUpdate);
		} catch (ParseException e1) {
			// TODO Auto-generated catch block
			System.out.println("Date Exception! :(");
		}
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		@SuppressWarnings("unchecked")
		List<Event> events = (List<Event>) em.createQuery(getEvents).getResultList();
		em.close();
		List<Event> returnEvents = new ArrayList<Event>(events);
		for (Event e : events) {
			String[] eventCoords = e.getCords().split(":");
			eventPos.setLatitude(Double.parseDouble(eventCoords[0]));
			eventPos.setLongitude(Double.parseDouble(eventCoords[1]));
			distance = geoCalc.calculateGeodeticCurve(reference, userPos, eventPos).getEllipsoidalDistance();
			if (distance > radius) {
				returnEvents.remove(e);
			}
			if (e.getEnd().before(new Date())) {
				returnEvents.remove(e);
			}
			if (ids != null) {
				if (ids.contains(e.getId())) {
					System.out.println("Zeitformat: e.getEdited = " + e.getEdited() + " // lastUserUpdate = " + lastUserUpdate);
					System.out.println("e.getEdited equals lastUserUpdate = " + e.getEdited().toString().equals(lastUserUpdate.toString()));
					if (lastUserUpdate != null
							&& (e.getEdited().before(lastUserUpdate) || e.getEdited().toString().equals(lastUserUpdate.toString()))) {
						returnEvents.remove(e);
						System.out.println("Remove: " + e.getId());
					}
				}
			}
			if (ids == null || !ids.contains(e.getId())) {
				if (e.getDeleted() != null) {
					returnEvents.remove(e);
				}

			}
		}
		System.out.println(gs.toJson(returnEvents));
		return gs.toJson(returnEvents);

	}

	/**
	 * Returns picture or pictures with id as JsonImageString
	 * 
	 * @param id
	 * @return JsonImageString
	 */
	@Path("images/{id}")
	@GET
	@Produces("application/json")
	public String getImages(@PathParam("id") int id) {
		System.out.println("Image loaded: " + id);
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		ImageHolder ih = (ImageHolder) em.find(ImageHolder.class, id);
		if (ih != null) {
			return ih.getJsonImages();
		}
		return "";
	}

	/**
	 * Save image to Database with id
	 * 
	 * @param id
	 * @param content
	 *            image as jsonimagestring
	 */
	@Path("images/{id}")
	@POST
	@Consumes("application/json")
	public void postImage(@PathParam("id") int id, String content) {
		System.out.println("BILD WIRD VERSUCHT ZU ADDEN! ID: " + id);
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("SocialmapServer");
		EntityManager em = emf.createEntityManager();
		ImageHolder ih = (ImageHolder) em.find(ImageHolder.class, id);
		em.getTransaction().begin();
		if (ih == null) {
			ImageHolder i = new ImageHolder(id, content);
			em.persist(i);
		} else {
			ih.setJsonImages(content);
		}
		em.flush();
		em.getTransaction().commit();
		em.close();

	}

}